package parser;

import org.w3c.dom.*;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;

/**
 * <p>description: </p>
 *
 * @author chenrui
 * @since 2019-04-08
 */
public class XmlParser {


    public void parse(InputStream inputStream)  {
        try {
            Document doc = createDocument(inputStream);
            process(doc.getLastChild());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用的是递归处理方法，类似于二叉树的前序遍历方式。
     * @param node 处理xml文件的根节点的
     *             比如sqlMapConfig.xml文件的sqlmap结点，因为sqlmap结点是根节点
     */
    private void process(Node node) {
        if (node instanceof Element) {
            // 处理当前结点
            String elementName = node.getNodeName();
            System.out.println("处理当前结点名称 " + elementName);

            // 处理当前结点中的属性
            NamedNodeMap attributes = node.getAttributes();
            int n = attributes.getLength();
            for (int i = 0; i < n; i++) {
                Node att = attributes.item(i);
                String attrName = att.getNodeName();

                System.out.println("处理当前结点中的属性的名称 " + attrName);
            }

            // 处理当前结点下的孩子结点，直到当前结点的孩子结点全部处理完毕，才会进行当前结点的下一个结点处理
            NodeList children = node.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                process(children.item(i));
            }

        } else if (node instanceof Text) {
            // Text
        }
    }

    /**
     * 根据读取的xml数据流，根据对应的xml数据流转为对应的xml文件
     * @param inputStream
     * @return
     * @throws ParserConfigurationException
     * @throws FactoryConfigurationError
     * @throws SAXException
     * @throws IOException
     */
    private Document createDocument(InputStream inputStream)
            throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        factory.setNamespaceAware(false);
        factory.setIgnoringComments(true);
        factory.setIgnoringElementContentWhitespace(false);
        factory.setCoalescing(false);
        factory.setExpandEntityReferences(true);

        DocumentBuilder builder = factory.newDocumentBuilder();
        builder.setErrorHandler(new ErrorHandler() {
            public void error(SAXParseException exception) throws SAXException {
                throw exception;
            }

            public void fatalError(SAXParseException exception) throws SAXException {
                throw exception;
            }

            public void warning(SAXParseException exception) throws SAXException {
            }
        });

        return builder.parse(new InputSource(inputStream));
    }

}
